package cn.yogehaoren.agile_gateway.util;

import cn.hutool.core.collection.ConcurrentHashSet;
import cn.yogehaoren.agile_gateway.entity.ServiceAddress;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.client.KubernetesClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author WangNing
 * @since 1.0
 */

@Component
@Slf4j
public class K8SServiceManager {

    static private final String PATTERN_STRING_1 = "service-(ws|http|https|wss)://(.*)/(.*):(\\d+)";
    static private final Pattern SERVICE_PATTERN_1 = Pattern.compile(PATTERN_STRING_1);

    static private final String PATTERN_STRING_2 = "service-(ws|http|https|wss)://(.*):(\\d+)";
    static private final Pattern SERVICE_PATTERN_2 = Pattern.compile(PATTERN_STRING_2);

    /**
     * 管理Route对应Service中的地址
     *
     * RouteId -> Service Address
     */
    private static final Map<String, ServiceAddress> SERVICE_ADDRESS_MAP = new HashMap<>();

    private  final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
    private final ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();;

    /**
     * 防止监听器重复
     * 格式为 {命名空间}:{service名}
     */
    private static final Set<String> watcherList = new ConcurrentHashSet<>();

    @Autowired
    KubernetesClient kubernetesClient;

    /**
     * 获取 Service 对应的Address
     * @param routeId 路由ID
     * @param routeURI 路由定义的Service
     * @return 路由地址  | null 找不到对应的service
     */
    public ServiceAddress getServiceAddress(String routeId, String routeURI){

        try{
            log.trace("==> 获取读锁");
            readLock.lock();

            // 有该RouteID 对应的地址缓存
            if(SERVICE_ADDRESS_MAP.containsKey(routeId)){
                return SERVICE_ADDRESS_MAP.get(routeId);
            }

        }finally {

            log.trace("<== 释放读锁");
            readLock.unlock();

        }


        String namespace = "";
        String serviceName = "";
        String port = "";
        String scheme = "";
        // 抽取 namespace 和 serviceName
        if(routeURI.matches(PATTERN_STRING_1)){
            Matcher matcher = SERVICE_PATTERN_1.matcher(routeURI);
            if(matcher.find()){
                scheme = matcher.group(1);
                namespace = matcher.group(2);
                serviceName = matcher.group(3);
                port = matcher.group(4);
            }
        } else if(routeURI.matches(PATTERN_STRING_2)){
            Matcher matcher = SERVICE_PATTERN_2.matcher(routeURI);
            if(matcher.find()){
                namespace = "default";
                scheme = matcher.group(1);
                serviceName = matcher.group(2);
                port = matcher.group(3);
            }
        }

        if(!StringUtils.isEmpty(namespace)&&
                !StringUtils.isEmpty(serviceName) &&
                !StringUtils.isEmpty(port)&&
                !StringUtils.isEmpty(scheme)){
            return getK8SService(namespace, serviceName, scheme, port,routeId);
        }


        return null;
    }



    private ServiceAddress getK8SService(String namespace, String serviceName, String scheme, String port , String routeId){

        Service service = kubernetesClient.services().inNamespace(namespace).withName(serviceName).get();
        if(service == null){
            return null;
        }
        String clusterIP = service.getSpec().getClusterIP();
        try{
            ServiceAddress serviceAddress = ServiceAddress.builder().scheme(scheme).host(clusterIP).port(Integer.valueOf(port)).build();


            log.debug("获取 service[{}:{}] ClusterIP: {}", namespace, serviceName, clusterIP);
            // 放置监听器当service改变时， 清除service缓存
            String setKey = String.format("%s:%s", namespace, serviceName);
            if(!watcherList.contains(setKey)){

                log.trace("添加 {}:{} 监听器", namespace, serviceName);
                kubernetesClient.services().inNamespace(namespace).watch(new ServiceWatcher(readWriteLock.writeLock(), SERVICE_ADDRESS_MAP, routeId, serviceName, scheme, Integer.valueOf(port)));
                watcherList.add(setKey);

            }

            return serviceAddress;

        }catch (NumberFormatException e){
            return null;
        }


    }


    /**
     * 去除监控器 Key
     */
    public void removeWatcherSetKey(String namespace, String serviceName){
        String setKey = String.format("%s:%s", namespace, serviceName);
        watcherList.remove(serviceName);
    }






}
